﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Windows.Forms;
using System.IO;
using System.Media;

namespace DrinkcounterUi
{
    public partial class Form1 : Form
    {
        private readonly Timer _updateTimer;
        private readonly Dictionary<string, Person> _players = new Dictionary<string, Person>();
        private Font _buttonFont;
        private Rectangle _bounds;
        private readonly List<SoundPlayer> _sounds = new List<SoundPlayer>();
        private IProtocol _protocol;
        private Timer _googleTimer;
        private bool _chartOrGauge = false;

        public Form1()
        {
            InitializeComponent();

            InitializeProtocol();
           
            _buttonFont = new Font(Properties.Settings.Default.Font, 50);
            //fullScreen();
			
			LoadSounds();

            Update(null, null);
            GetImages(null, null);

            _updateTimer = new Timer();
            _updateTimer.Interval = 15000;
            _updateTimer.Tick += Update;
            _updateTimer.Start();

            _googleTimer = new Timer();
            _googleTimer.Interval = 15000;
            _googleTimer.Tick += GetImages;
            _googleTimer.Start();
        }

        private void InitializeProtocol()
        {
            string protostring = Properties.Settings.Default.Protocol;
            if (protostring.Equals("toni", StringComparison.InvariantCultureIgnoreCase))
            {
                _protocol = new ToniProtocol();
                Error("Yhdistetään: tonin protokolla", null);
            }
            else if (protostring.Equals("heikki", StringComparison.InvariantCultureIgnoreCase))
            {
                _protocol = new HeikkiProtocol();
                Error("Yhdistetään: " + Properties.Settings.Default.HeikinUpdateUrl, null);
            }
            else
            {
                MessageBox.Show("Invalid protocol, accepted values are toni, heikki");
                Close();
                return;
            }

        }

        private void LoadSounds() 
		{
			const string dir = "Sounds";
			if (!Directory.Exists(dir)) {
				return;
			}
			
			DirectoryInfo di = new DirectoryInfo(dir);
			foreach (FileInfo fi in di.GetFiles("*.wav")) {
				try {
					SoundPlayer player = new SoundPlayer(fi.FullName);
					player.Load();
					_sounds.Add(player);
				} catch {
				
				}
			}
		}
		
		private void PlaySound()
		{
			if (Properties.Settings.Default.Sounds && _sounds.Count > 0) {
                Random r = new Random();
				int i = r.Next(_sounds.Count);
				_sounds[i].Play();
			}
		}

        private void Update(Object o, EventArgs e)
        {
            List<Person> newdata = null;
            try
            {
                newdata = _protocol.GetPersons();
            }
            catch (Exception ex)
            {
                Error("Ei pystytty lukemaan tietoja intter netistä.", ex);
                return;
            }

            if (newdata.Count == 0)
            {
                Error("Ei rekisteröityjä juojia.", null);
                return;
            }

            infoLabel.Hide();

            UpdatePersons(newdata);

            // check for removed players
            List<string> removeKeys = (from i in _players.Keys let found = newdata.Any(p => p.Id == i) where !found select i).ToList();

            foreach (string i in removeKeys)
            {
                playerPanel.Controls.Remove(_players[i].Button);
                _players.Remove(i);
            }

            // modify texts
            foreach (Person p in _players.Values)
            {
                p.Button.Text = p.Name + "\n" + p.Drinks + " ryyppyä\n" + p.Alcohol.ToString("F") + " ‰\n" + p.Idletime;
            }

            UpdateButtons();
        }

        private void UpdatePersons(List<Person> newdata)
        {
            // iterate new data
            foreach (Person p in newdata)
            {
                if (_players.Keys.Contains(p.Id))
                {
                    // update old entry
                    Person old = _players[p.Id];
                    old.Alcohol = p.Alcohol;
                    old.Drinks = p.Drinks;
                    old.Name = p.Name;
                    old.Idletime = p.Idletime;
                }
                else
                {
                    // add new entry
                    Button b = new Button();
                    b.BackColor = p.Color;
                    b.Click += new EventHandler(ButtonClicked);
                    playerPanel.Controls.Add(b);
                    p.Button = b;
                    _players.Add(p.Id, p);
                }
            }
        }

        private void Error(string msg, Exception exception)
        {
            foreach (var value in _players.Values)
            {
                playerPanel.Controls.Remove(value.Button);
            }

            _players.Clear();
            
            infoLabel.Text = msg;
            infoLabel.Show();
            if (exception != null)
                Console.Out.WriteLine(exception);
        }


        private void ButtonClicked(object sender, EventArgs e)
        {
            Person person = _players.Values.FirstOrDefault(p => p.Button == sender);

            try
            {
                _protocol.AddDrink(person.Id);

                person.Button.BackColor = Color.DarkGray;
                person.Button.Enabled = false;
                person.Locked.Start();

                PlaySound();
            } catch (Exception ex)
            {
                Error("FAIL! Valita Laurille tai " + Properties.Settings.Default.Protocol.Replace("kk", "k") + "lle!", ex);
            }

            Update(null, null);
            GetImages(null, null);
        }

        private void UpdateButtons()
        {
            const int gap = 10;
            int width = playerPanel.Width;
            int height = playerPanel.Height;
            int buttonwidth = width;
            int buttonheight = height;
            int count = _players.Count;
            int rows = 1;
            int cols = 1;

            switch (count)
            {
                case 0:
                    return;
                case 1:
                    break;
                case 9:
                    cols = 3;
                    rows = 3;
                    buttonwidth = (width - (cols - 1) * gap) / cols;
                    buttonheight = (height - (rows - 1) * gap) / rows;
                    break;
                default:
                    while (rows * cols < count)
                    {
                        if (buttonwidth > buttonheight)
                        {
                            cols++;
                            buttonwidth = (width - (cols - 1) * gap) / cols;
                        }
                        else
                        {
                            rows++;
                            buttonheight = (height - (rows - 1) * gap) / rows;
                        }
                    }
                    break;
            }

            int curcol = 0;
            int currow = 0;
            int fontsize = Math.Max(buttonheight / 12, 6);
            _buttonFont = new Font(_buttonFont.FontFamily, fontsize, _buttonFont.Style);
            
            foreach (Person p in _players.Values)
            {
                p.Button.Size = new Size(buttonwidth, buttonheight);
                p.Button.Location = new Point(buttonwidth * curcol + curcol * gap, buttonheight * currow + currow * gap);
                p.Button.Font = _buttonFont;
                //if (Properties.Settings.Default.UseColors) p.Button.BackColor = colors[p.Id % colors.Count];
                curcol++;
                if (curcol >= cols)
                {
                    curcol = 0;
                    currow++;
                }
            }
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            UpdateButtons();
        }

        private void FullScreen()
        {
            // if the window is in normal mode (sizable) change to full window mode
            if (FormBorderStyle == FormBorderStyle.Sizable)
            {
                // disable the window border
                this.FormBorderStyle = FormBorderStyle.None;

                // safe the current position and size
                _bounds = this.Bounds;

                // resize the window to full screen size and make it top most
                this.Bounds = Screen.PrimaryScreen.Bounds;
                this.TopMost = true;
            }
            else // the window is already in full mode switch back to normal mode
            {
                // set the position and size back to the value before full window mode
                this.Bounds = _bounds;

                // enable the window border
                this.FormBorderStyle = FormBorderStyle.Sizable;
            }
        }

        protected override bool ProcessDialogKey(Keys keyData)
        {
            if (keyData == Keys.F11)
            {
                FullScreen();
            }
            return base.ProcessDialogKey(keyData);
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            Properties.Settings.Default.Save();
        }

        private void GetImages(object sender, EventArgs e)
        {
            int toggle = Properties.Settings.Default.ChartGaugeBoth;
            _chartOrGauge = !_chartOrGauge;

            if (toggle == 0 || (toggle == 2 && _chartOrGauge))
            {
                var dict = GetHistories();
                if (dict != null)
                {
                    foreach (var player in _players.Values)
                    {
                        if (!dict.ContainsKey(player.Name)) continue;
                        Image i = Google.GetChart(player, dict[player.Name], (int)(player.Button.Width * 0.9), Properties.Settings.Default.ChartMaxMinutes, Properties.Settings.Default.ChartMaxAlcohol, Color.FromArgb(255, 50, 50, 50));
                        player.Button.Image = i;
                    }
                }
            }
            else
            {
                foreach (var player in _players.Values)
                {
                    Image i = Google.GetGauge(player, (int)(player.Button.Width), Properties.Settings.Default.GaugeMaxValue, Color.FromArgb(255, 50, 50, 50));
                    player.Button.Image = i;
                }
            }
        }

        public Dictionary<string, List<PersonHistory>> GetHistories()
        {
            try
            {
                string url = Properties.Settings.Default.HistoryUrl;

                string data = DownloadData(url);
                string[] rows = data.Split('\n');
                
                Dictionary<string, List<PersonHistory>> d = new Dictionary<string, List<PersonHistory>>();


                for (int index = 1; index < rows.Length; index++)
                {
                    var row = rows[index];

                    string[] columns = row.Split(',');
                    if (columns.Length < 4) continue;

                    PersonHistory ph = new PersonHistory
                                           {
                                               Timestamp = DateTime.Parse(columns[0]),
                                               Name = columns[1].Replace("\"", ""),
                                               Alcohol = double.Parse(columns[2], CultureInfo.InvariantCulture),
                                               Drinks = int.Parse(columns[3])
                                           };

                    if (!d.ContainsKey(ph.Name))
                    {
                        d[ph.Name] = new List<PersonHistory>();
                    }

                    var list = d[ph.Name];

                    list.Add(ph);
                }

                return d;
            } 
            catch (Exception e)
            {
                Console.WriteLine(e);
                return null;
            }
        }

        /// <summary>
        /// Downloads the data from specified url.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <returns>data if found, null otherwise</returns>
        public string DownloadData(string url)
        {
            HttpWebRequest request = null;
            WebResponse response = null;
            Stream stream = null;
            StreamReader reader = null;

            string data;

            try
            {
                request = (HttpWebRequest)WebRequest.Create(url);

                response = request.GetResponse();
                stream = response.GetResponseStream();
                reader = new StreamReader(stream);

                data = reader.ReadToEnd();
            }
            finally
            {
                try { reader.Close(); }
                catch { }
                try { stream.Close(); }
                catch { }
                try { response.Close(); }
                catch { }
            }

            return data;
        }
    }
}
